﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection;
using System.Net.Http;
using System.Text.Json;

namespace AspNetCore.Host.Hosting
{
    public class KestrelOptions
    {
        public string Url { get; set; }
        public int RequestBodyMaxSize { get; set; }
    }
    internal class Kestrel
    {
        private readonly ILogger _logger;
        private readonly KestrelOptions _options;
        private readonly IWebHostEnvironment _environment;
        private readonly IServiceProvider _serviceProvider;
        public Kestrel(
            ILogger<Kestrel> logger,
            IWebHostEnvironment environment,
            IServiceProvider serviceProvider,
            IOptions<KestrelOptions> options)
        {
            _logger = logger;
            _options = options.Value;
            _environment = environment;
            _serviceProvider = serviceProvider;
        }


        public void Run(IApplicationBuilder applicationBuilder)
        {
            var serverSocket = new Socket(
                AddressFamily.InterNetwork,
                SocketType.Stream,
                ProtocolType.Tcp);
            var app = ((ApplicationBuilder)applicationBuilder).Build();
            var uri = new Uri(_options.Url);
            var ipaddress = IPAddress.Parse(uri.Host);
            var ipEndpoint = new IPEndPoint(ipaddress, uri.Port);
            serverSocket.Bind(ipEndpoint);
            serverSocket.Listen();
            Logging();
            Socket client;
            while (true)
            {
                try
                {
                    client = serverSocket.Accept();
                    new Thread((obj) =>
                    {
                        Socket? client = (obj as Socket) ?? throw new ArgumentException(nameof(obj));
                        try
                        {
                            ReceiveClientRequestAsync(client, app).Wait();
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError(ex.Message, ex);
                        }

                    }).Start(client);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex.Message, ex);
                }
            }
        }
        /// <summary>
        /// 1.一个请求一个线程，（请求开始->响应结束）
        /// 2.一个请求一个scope（请求开始->响应结束）
        /// </summary>
        /// <param name="client"></param>
        /// <param name="app"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        private async Task ReceiveClientRequestAsync(Socket client, RequestDelegate app)
        {
            var buffer = new byte[_options.RequestBodyMaxSize];
            int cout = client.Receive(buffer);
            //HttpContext
            var package = Encoding.UTF8.GetString(buffer, 0, cout);
            var request = JsonSerializer.Deserialize<HttpRequest>(package)
                ?? throw new ArgumentException();
            using (var scope = _serviceProvider.CreateScope())
            {
                var context = new HttpContext(request, scope.ServiceProvider);
                await app(context);
                var responseData = context.Response.GetBuffer();
                buffer = context.Response.GetBuffer();
                client.Send(responseData);
            }
        }


        private void Logging()
        {
            _logger.LogInformation("Now listening on: {0}", _options.Url);
            _logger.LogInformation("Application started. Press Ctrl+C to shut down.");
            _logger.LogInformation("Hosting environment: {0}", _environment.EnvironmentName);
        }
    }
}
